#!/usr/bin/perl

package Peekable;

use IO::Handle;
use Carp qw(croak);

sub new {
    my $class = ref($_[0]) || $_[0] || "Peekable";
    my $fh = undef;
    @_ == 1 or croak "usage: new $class";
    bless { fh=>$fh, buf=>"", pos=>0 }, $class;
}

sub new_from_fd {
    my $class = ref($_[0]) || $_[0] || "Peekable";
    @_ == 3 or croak "usage: new_from_fd $class FD, MODE";
    my $fh = gensym();
    shift;
    IO::Handle::fdopen($fh, @_) or return undef;
    bless { fh=>$fh, buf=>"", pos=>0 }, $class;
}

sub set_fd {
    my $self = $_[0];
    @_ == 2 or croak "usage: new_from_fd $class FD, MODE";
    if (defined $self->{fh}) { close ($self->{fh}); }
    $self->{fh} = $_[1];
    $self->{buf} = "";
    $self->{pos} = tell($_[1]) || 0;

#    print \{$self->{fh}}, "\n";

    return $self;
}


sub peek {
    my $self = $_[0];
    @_ == 3 or croak "usage: $fh->peek BUFFER, LENGTH";
    
    my $size = $_[2];
    if ( $size <= 0 ) { return 0; }
    if ( $size > length($self->{buf}) ) {
	my $newbuf;
	while ( read($self->{fh}, $newbuf, $size-length($self->{buf})) ) {
	    $self->{buf} .= $newbuf;
	}
    }
    if ( $size > length($self->{buf}) ) {
	$size = length($self->{buf});
    }
    $_[1] = substr($self->{buf}, 0, $size);
    return $size;
}

sub skip {
    my $self = $_[0];
    my $mysize = 0;
    my $swapsize = 0;
    my $buf;

    @_ == 2 or croak "usage: $fh->skip SIZE";
    
    my $size = $_[1];
    if ( $size <= 0 ) { return 0; }
    if ( length($self->{buf}) > 0 ) {
	$mysize = $size;
	$mysize = length($self->{buf}) if $mysize > length($self->{buf});

        substr($self->{buf}, 0, $mysize) = "";
	$size -= $mysize;
	$swapsize += $mysize;
	$self->{pos} += $mysize;
    }

    while ( $size > 0 ) {
	$mysize = $size;
	$mysize = 2048 if ( $mysize > 2048 );
	
	$mysize = read($self->{fh}, $buf, $mysize);
	last if !$mysize; 

	$size -= $mysize;
	$swapsize += $mysize;
	$self->{pos} += $mysize;
    }

    return $swapsize;
}

sub tell {
    @_ == 1 or croak 'usage: $fh->tell()';

    return $_[0]->{pos};
}

sub close {
    @_ == 1 or croak 'usage: $fh->close()';

    if ( $_[0]->{fh} ) { $_[0]->{fh}->close; }
    $_[0]->{fh} = undef;
    $_[0]->{buf} = "";
    $_[0]->{pos} = 0;
    

}


1;
